Isetta programming manual ------------------------- 3-1-2025 started 10-1-2025 updated 11-1-2025 new blitter codes 9-2-2025 debugger function working 12-3-2025 manual published on hackaday.io page 20-3-2025 added copycnv to blitter 6-5-2025 blitter remarks 9-6-2025 add PSG sound system 1-7-2025 changed blitter port values 23-8-2025 add mouse wheel and RTC Chapter 1 I/O ports Chapter 2 Bankswitching Chapter 3 Video system Chapter 4 Blitter Chapter 5 Sound system Chapter 6 Debugger support Chapter 1 I/O ports -------------------- Just as in the Z80 and 8080, Isetta can input or output a single byte at I/O ports. There are 256 possible ports for input and 256 for output. An I/O instruction is two bytes long: <0xD3> OUT instruction: This will output the byte in ACC to the specified port number <0xDB> IN instruction: This will input a byte from the specified port number and put it in ACC. In Isetta, these IN and OUT instructions are also in the 6502 instruction set, with the same opcodes 0xD3 and 0xDB. Using undefined I/O port numbers will, in the current version, most likely result in a crash. I/O ports - general ------------------- port_in_config = 0xE4; // INPUT this tells what the HW configuration is. For the current version, always zero. port_in_microcode_year = 0xE6 ; // INPUT reads the microcode date from microcode flash (reads from flash page 6). port_in_microcode_month = 0xE7 ; port_in_microcode_day = 0xE8 ; port_out_isa_6502 = 0x0A; // OUTPUT switch instruction set to 6502. Preserves Accumulator and PC. port_out_isa_Z80 = 0x0B; // OUTPUT switch instruction set to Z80. Preserves Accumulator and PC. Instead of switching instruction set with writing an arbitrary value to the above ports, it is also possible to write to "port_io_reg_isa" with the ISA number in ACC. Reading from this port gives the current ISA. port_io_reg_isa = 0x10; Supported values for port_io_reg_isa are: const ISA_6502 = 0x38; const ISA_Z80 = 0x39; const ISA_BLIT = 0x3A; // Blitter const ISA_PSG = 0x3B; // Sound generator To terminate your user program, you can write a value to port_out_shutdn. value 00: Isetta will reset value 01: Isetta will shut down. The video screen will switch off. (But the board and chips still have power) Use reset-button to power-up again. port_out_shutdn = 0x1C For a correct shutdown, use the following code: di ; disable interrupt ld a,7 out $40 ; psg select register 7 ld a,$3F out $41 ; psg tones and noise off ld a,1 out $1C ; video off wait: jr wait ; wait until reset. I/O ports - Character I/O and Mouse ----------------------------------- port_in_scancode = 0x31; // IN read PS/2 keyboard scancode (bits reversed) or byte from RPi. // This input reads from a 256 byte buffer. // Returns ACC=zero if the buffer is empty. port_out_tx = 0x31; // OUT sends a byte to the Raspberry Pi (RPi). RPi has to provide the clock. // if ACC=0 after this instruction, the buffer was full and the OUT must be retried. port_in_mouse = 0x32; // IN receive a byte from the PS/2 mouse (bits reversed). // This input reads from a 256 byte buffer. // Returns Z=1 if the buffer was empty. Information about the PS/2 protocol can be found here: https://www.eecg.utoronto.ca/~jayar/ece241_08F/AudioVideoCores/ps2/ps2.html The mouse must be enabled by giving it a 0xF4 command, Isetta does that always at start-up. The mouse wheel is also supported. The mouse sends messages that are 4 bytes long. The mouse wheel is explained here: https://users.utcluj.ro/~baruch/sie/labor/PS2/PS-2_Mouse_Interface.htm Several PS/2 keyboards must also be enabled before they can operate, Isetta does that also at startup. I/O ports - SPI functions ------------------------- port_spi1_start = 0x32; // OUT start transaction on SPI-1 port_spi2_start = 0x33; // OUT start transaction on SPI-2 port_spi_out = 0x37; // OUT send byte (in ACC) to SPI device port_spi_in = 0x3A; // IN read byte from SPI device to ACC port_spi_idle = 0x3F; // OUT stop transaction port_spi_state = 0x3C; // IN read status byte from SPI. Sets Z flag if SPI active. Note 1) spi1_start has the same port number as in_mouse. But since the first one is output and the other one input, there is no conflict. Note 2) While a SPI transaction is in progress, the RST #38 interrupts are disabled (by microcode). I/O ports - Sound ----------------- port_out_sound = 0x30; // OUT output a byte to the sound_output. Effective at next scanline interrupt. I/O ports - ZX Spectrum specific --------------------------------- port_io_zx_spectrum = 0xFE // IN/OUT port_io_zx_joystick = 0x1F // IN reserved for joystick. Unused. port_io_zx_128 = 0xFD // reserved for OUT, ZX_Spectrum 128 paging system I/O ports - RTC Real time clock -------------------------------- Isetta has a real-time clock and calendar. The clock can be read with an IN instruction on port "port_io_rtc_read". Before this instruction, the HL register must have the address of an 8-byte memory section "rtc_time" , where the actual time and date will be placed by the IN instruction. Definition of this memory section is: rtc_sec equ rtctime+0 rtc_min equ rtctime+1 rtc_hrs equ rtctime+2 rtc_day equ rtctime+3 rtc_month equ rtctime+4 rtc_year equ rtctime+5 ; and also +6 (2 bytes for the year) rtc_magic equ rtctime+7 All values start at 0, except day and month that start at 1. The first byte of the year is 0 - 99, the second byte is the number of ages (in most cases this is 20). rtc_magic is 0xA3 if the time was initialized. After a power-off, this is a random value, and the time then is also random. But after a reset or shutdown, the RTC keeps working. The clock can be set to a new value by placing that value in the rtc_time structure, set rtc_magic to 0xA3 to indicate that the time/date is valid, set HL to point to that structure, and then do an OUT instruction on port "port_io_rtc_write". port_io_rtc_read = 0xCC port_io_rtc_write = 0xCC Chapter 2 Bankswitching ------------------------ There are 8 memory banks (of 64kByte each) in RAM. Each bank has 4 blocks of 16 kByte. In this section, the hardware bank numbers are used. Software may define bank numbers different. After startup, your Z80 or 6502 program will normally run in HW bank 3. Use of the memory banks: HW bank 0 system data, Hires screen buffer (upper screen half) HW bank 1 system data, partially free to use (see below) HW bank 2 system data, Hires screen buffer (lower screen half) HW bank 3 Main Z80/6502 bank (SymbOs bank0) HW bank 4 free to use (SymbOs bank1) HW bank 5 free to use (SymbOs bank2) HW bank 6 free to use (SymbOs bank3) HW bank 7 free to use (SymbOs bank4) Transfer a single byte to/from another bank: port_io_bank0 = 0xF0 // IN/OUT read/write byte (in ACC) from/to memory location (in HL) in HW bank0. port_io_bank1 = 0xF1 // same for HW bank1 port_io_bank2 = 0xF2 // same for HW bank2 port_io_bank3 = 0xF3 // same for HW bank3 In general, all memory in HW banks 3 - 7 are free for use by a user program or operating system. However, in HW bank1 (SymbOs bank6) there are two ranges that can also be used: - 0x0000 - 0x03FF - 0xMM00 - 0xFFFF The value MM is obtained by reading the input port_in_bnk1_free: port_in_bnk1_free = 0x27 (The obtained value can be different, depending on the microcode version) For bankswitching, the "out (c),reg" instruction is used. "reg" can be any register a,b,c,d,e,h,l. For the moment, this is only available in the Z80 instruction set. port_out_system = 0x18; // OUTPUT default is 0x00. Set it to 0x40 to enable bankswitching. OUTPUT out (c),reg: set bank configuration according to register "reg": ( C must be 11xx xxxx) reg = 11000000 ; // The Z80/6502 only sees HW bank 3 (this is the default). (C0) reg = 11BBB010 ; // The Z80/6502 only sees the memory bank according to BBB (see table). (C2) reg = 11BBB1LL ; // The Z80/6502 only sees bank 3, but in the range 0x4000 - 0x7FFF it sees // block number LL from the memory bank selected with BBB (see table). (C4-7) reg = 11BBB001 ; // The Z80/6502 only sees bank 3, but in the range 0xC000 - 0xFFFF it sees // block number 3 from the memory bank selected with BBB (see table). But it can not // execute from that block. (C1) table: BBB LL 000 HW bank 4 00 block 0 (0x0000-0x3FFF) 001 HW bank 5 01 block 1 (0x4000-0x7FFF) 010 HW bank 6 10 block 2 (0x8000-0xBFFF) 011 HW bank 7 11 block 3 (0xC000-0xFFFF) 100 HW bank 0 101 HW bank 1 110 HW bank 2 111 HW bank 3 There will be a new port to do bankswitching (after 18-3-2025), the register must be A: port_out_sbnk = 0x1B; // OUTPUT reg value in register A. todo: - output to set HL register (6502) - have bankswitch instruction for 6502 Chapter 3 Video system ----------------------- The video system uses a "frame table" that describes what should happen at every scanline. There are 525 scanlines in a normal VGA screen (400 or 480 visible), so the table is 525 bytes long. The microcode part that writes to the screen will read a action-code byte from this table at the start of each scanline, and that byte determines the action that the microcode will execute. Frame table location: 0x0300 in HW bank 1. (but it can be moved to another position, in many cases it will be 0x0700). possible actioncodes: v_prepare equ $24 ; At very first frame table location. Initializes for the visible scanlines that follow. v_end equ $25 ; This has to follow the last visible scanline v_blank equ $26 ; This is a line in blanking period. Almost all time in these lines is used for Z80/6502 processing. v_wait equ $27 ; This must be in the line just before changing the vertical sync signal v_act_vsync equ $2A ; At this line, vertical sync is activated v_deact_vsync equ $2B ; At this line, vertical sync is de-activated v_repeat equ $2C ; At the end of the frame table, causes a jump back to the start of the frame table v_sound equ $64 ; This causes the sound processor to process a series of 256 samples (if enough space in buffer) And here are the actioncodes that define the video mode at each visible scanline: v_hires equ $2D ; This causes a line of high-resolution video to be written to the screen v_spectrum equ $0D ; This causes a line of ZX-spectrum video to be written to the screen v_pix_320 equ $34 ; This causes a line of 320 pixels to be written to the screen In general, the codes for the video mode also refer to a video line and attribute line: (example for line n) 0x0300 + n (bank1) ; this byte has the action code 0x0300 + n (bank0) ; this byte has the LSB of the video line data address 0x0300 + n (bank2) ; this byte has the MSB of the video line data address 0x0600 + n (bank1) ; this byte has, for ZX spectrum mode, the MSB of the video attribute line. Hires video mode ---------------- The hires mode has a resolution of 640 x 400 pixels, with 16 colors for every pixel. For the hires mode, the video line is 320 bytes long. Each byte has a first pixel in the high nibble, and a second pixel in the low nibble, so the video line is 640 pixels long. The screen is divided in the upper half and the lower half. For the upper half, the video lines are in HW bank 0 starting at 0x0600, up to 0xFFFF (200 upper half lines). For the lower half, the video lines are in HW bank 2 starting at 0x0600, up to 0xFFFF (200 lower half lines). So, the video data is almost 128 kBytes. Since the video line address is 17 bits, it is stored in a special way in the table. Supposing that the address is always even, it is shifted one bit to the right. The upper bit is then filled with a "0" if it is in the upper half and a "1" if it is in the lower half of the screen. The pixel color in the nibble is defined as "DRGB", where R,G,B bits define if the R,G,B components are each on or off. The "D" bit will, if set to 1, result in a dark version of the specified color. Note that, since the start address of each video line is in the frame table, scrolling up or down can be very fast, since only the start addresses have to be changed in the table. Horizontal scrolling can also be very quick. 320 pixel video mode -------------------- This mode has a resolution of 320 x 200 pixels, with 64 colors for every pixel. This is not tested yet. ZX Spectrum video mode ---------------------- The ZX spectrum video mode has a resolution of 256 x 192 pixels with 2 colors. For each block of 8 x 8 pixels there is an attribute byte, that defines the 2 possible colors for these 64 pixels. Each video line is located in the normal addressing range of the processor (HW bank 3), starting at 0x4000 (up to 0x57FF). Each line is 32 bytes long, with 8 pixels per byte this defines the 256 pixels of a line. The starting address of a video line is in the frame table as described before. The attribute bytes, also 32 bytes per line, are also in the normal addressing range of the processor, starting at 0x5800 (up to 0x5AFF). The LSB of the address of this attribute byte is the same as the LSB of the pixel data. The MSB of the attribute byte is at location "0x0600 + n (bank1)" as described above. The layout of the video lines in memory is not straightforward, you can find it here: (also describes how color is encoded) http://www.breakintoprogram.co.uk/hardware/computers/zx-spectrum/screen-memory-layout The program "ZXOS.HEX" will set-up the frame table and other things to make Isetta run ZX Spectrum programs. Only the flashing of video data is not implemented (yet?). Chapter 4 Blitter ------------------ The Blitter is a microcoded unit that is specialized in writing to rectangular sections of the Hires screen. To use this, you first setup the parameters, using special OUTPUT ports. After that, you start the blitter by writing the action code to the P_BLITCTRL output port. Most commands require three kinds of parameters: (most parameters are 16 bits) - source base address, coordinates, line-length - screen coordinates - rectangle size A few commands use FILL1 and FILL2 that both specify a color. Blitter color conversion ------------------------ With the action code D_BMCOPYCNV "copy-convert" (see action codes), the blitter will convert colors from the SymbOs numbering (used in memory) to the Isetta numbering (used on the screen). Isetta colors: 0 black 1 blue 2 green 3 cyan 4 red 5 magenta 6 yellow 7 white 15 gray 8 dark // add this to the basic color number (except gray) to get a dark version SymbOs colors: 0 light yellow (no correct conversion possible) 1 black 2 orange (no correct conversion possible) 3 dark red 4 cyan 5 dark blue 6 light blue (converted to cyan) 7 blue 8 white 9 dark green 10 green 11 magenta 12 yellow 13 gray 14 light red (no correct conversion possible) 15 red Blitter Source parameters port numbers (SRC) -------------------------------------------- The source parameters can refer to a position in memory, or to a position on the screen. P_BLITSRCX_L equ #90 ; src_x_l X-coordinate (in pixels) P_BLITSRCX_H equ #91 ; src_x_h P_BLITSRCY_L equ #92 ; src_y_l Y-coordinate (in pixels) P_BLITSRCY_H equ #93 ; src_y_h P_BLITSRCA_L equ #B7 ; src_a_l Base address (24 bits) P_BLITSRCA_H equ #B8 ; src_a_h P_BLITSRCA_U equ #B9 ; src_a_u (upper byte of base address) P_BLITSRCL_L equ #BA ; src_l_l line length (bytes) P_BLITSRCL_H equ #BB ; src_l_h /* old values were: P_BLITSRCA_L equ #97 ; src_a_l Base address (24 bits) P_BLITSRCA_H equ #98 ; src_a_h P_BLITSRCA_U equ #9A ; src_a_u (upper byte of base address) P_BLITSRCL_L equ #9B ; src_l_l line length (bytes) P_BLITSRCL_H equ #9F ; src_l_h */ When the source is memory, Y-coordinate should be zero. The base address should be set, and src_a_u should be a value 0 - 7, where the value 0 is HW bank3, value 1 is HW bank4, etc (wrapping around). The source rectangle should be fully inside a single HW bank. The line length gives the number of bytes to be added to go to the next source line. For writing TEXT from a 8x8 font (where each byte represents a character row), the line length should be set to 1. When the source is a screen address, X and Y give the pixel coordinates, and the 24-bit address should be 0x800600 (The upper bit indicates that a screen address is used). The HW bank is selected automatically (screen rectangle can be partially on upper and lower screen half). The line length should be 320 (number of bytes to go to next line) When the action is D_BMFILL or D_BMFXOR, the source parameters are not needed, but src_x_l must be set to 0. Blitter Screen parameters port numbers (DST) -------------------------------------------- P_BLITDSTX_L equ #88 ; dst_x_l X-coordinate (in pixels) P_BLITDSTX_H equ #89 ; dst_x_h P_BLITDSTY_L equ #8A ; dst_y_l Y-coordinate (in pixels) P_BLITDSTY_H equ #8B ; dst_y_h /* old values were: P_BLITDSTX_L equ #A0 ; dst_x_l X-coordinate (in pixels) P_BLITDSTX_H equ #A1 ; dst_x_h P_BLITDSTY_L equ #A2 ; dst_y_l Y-coordinate (in pixels) P_BLITDSTY_H equ #A3 ; dst_y_h */ This is always a position on the Hires screen. The upper left pixel has coordinates (0,0). Screen contents is 4bpp (bits per pixel). Blitter Size parameters port numbers ------------------------------------ P_BLITSIZX_L equ #B0 ; size_x_l rectangle X-size in pixels P_BLITSIZX_H equ #B1 ; size_x_h P_BLITSIZY_L equ #B2 ; size_y_l rectangle Y-size in pixels P_BLITSIZY_H equ #B3 ; size_y_h Blitter Fill parameters port numbers ------------------------------------ P_BLITFILL1 equ #78 ; fill1 (4 bits) (also text foreground color). Use the SymbOs color numbers. P_BLITFILL2 equ #79 ; fill2 (4 bits) (also text background color). Use the SymbOs color numbers. Blitter Starting action ----------------------- There are several actions that the blitter can perform, they are started by writing the action code to the control port. The blitter will then take control, and after the operation is completed, control is given back to the Z80/6502 processor. P_BLITCTRL equ #EA ; port number for blitter control port Blitter Action codes -------------------- D_BMCOPY equ #08 ; COPY source to screen D_BMCOPYCNV equ #00 ; COPY source to screen, converting SymbOs colors to Isetta colors D_BMSKIP equ #04 ; As COPYCNV, but SymbOs color 0 is transparent D_BMSAVE equ #01 ; Save from screen to source memory address D_BMTEXT equ #09 ; TEXT write a character from a font map to the screen. Font map is 1bpp. D_BMFILL equ #2D ; Fill a screen rectangle, alternating colors FILL1 and FILL2 D_BMFXOR equ #2E ; Fill a screen rectangle using XOR operator, alternating colors FILL1 and FILL2 The following modifier codes can be added to the action code: D_BLITBACKX equ #0B ; Only for COPY. Action is performed right-to-left. (not implemented) D_BLITBACKY equ #80 ; Only for COPY. Action is performed bottom-to-top. D_BLITP14 equ #00 ; Only used with TEXT. Converts 1bpp to 4 bpp (bits per pixel) D_BLITP24 equ #02 ; Only used with COPYCNV or SKIP. Converts 2bpp to 4 bpp D_BLITP44 equ #00 ; Default, screen and source are both 4bpp After the action the blitter will add size_x_l to dst_x. So, after writing a character it is not needed to set the screen-x-coordinate to the next position. For 2bpp to 4bpp conversion: - 00 converts to 0000 (black) - 01 converts to FILL2 - 10 converts to FILL1 - 11 converts to FILL1 XOR FILL2 NOTE When D_BLITBACKY is used, the Y coordinates of src and dst must be the bottom coordinates. The blitter will decrement the Y coordinate after every processed line. NOTE (6-5-2025) before using COPYCNV you must put color 0 (lightyellow) in FILL1. NOTE (3-1-2025) The blitter doesn't work for 6502 yet. NOTE (23-4-2025) The 2bpp functions do not yet work correctly when src and dst are not well aligned NOTE (15-5-2025) The parameters are internally stored in registers similar to BC, DE, HL. But when an EXX swap is used after filling parameters, the parameters are also swapped, and the blitter action finds wrong parameter values. So better not use odd number of EXX between setting parameters and doing the action. NOTE The blitter only works if the bankswitching system is enabled. Chapter 5 Sound system ----------------------- Isetta has a sound system that is compatible with the PSG (programmable sound generator) AY-3-8910. The PSG has three channels, that each can generate a tone or noise, with adjustable volume each. The PSG has several 8-bit registers, R0 - R15. First write the register number to port_out_psg_reg, then write the data for that register to port_out_psg_data. It is not possible to read from a register. port_out_psg_reg equ 0x40 ; port number for PSG register port_out_psg_data equ 0x41 ; port number for PSG data Registers: 7654 3210 R0 PPPP PPPP Channel A tone period 7..0. R1 ---- PPPP Channel A tone period 11..8. R2 PPPP PPPP Channel B tone period 7..0. R3 ---- PPPP Channel B tone period 11..8. R4 PPPP PPPP Channel C tone period 7..0. R5 ---- PPPP Channel C tone period 11..8. R6 ---P PPPP Noise shift period. R7 I--- ---- unused -I-- ---- unused --C- ---- Mix Noise with Channel C, active low. ---B ---- Mix Noise with Channel B, active low. ---- A--- Mix Noise with Channel A, active low. ---- -C-- Enable Channel C, active low. ---- --B- Enable Channel B, active low. ---- ---A Enable Channel A, active low. R8 ---M ---- Channel A Mode, 0=level, 1=envelope. ---- LLLL Channel A Level. R9 ---M ---- Channel B Mode, 0=level, 1=envelope. ---- LLLL Channel B Level. R10 ---M ---- Channel C Mode, 0=level, 1=envelope. ---- LLLL Channel C Level. R11 PPPP PPPP Envelope period 7..0. R12 PPPP PPPP Envelope period 15..8. R13 ---- C--- Envelope shape "Continue" control. ---- -A-- Envelope shape "Attack" control. ---- --A- Envelope shape "Alternate" control. ---- ---H Envelope shape "Hold" control. R14 DDDD DDDD unused R15 DDDD DDDD unused The tone period numbers must be calculated for a 2.00 MHz clock frequency of the PSG: Tone_frequency = 2 MHz / (16 * period) Chapter 6 Debugger support --------------------------- Isetta has support for a debugger. The programmer has to provide the debugger code (Z80). The debugger code must be somewhere in the range 0x8000 - 0xFFFF. Let's call the MSB of the debugger code 0xMM When the debugger is switched on, the following will happen: - at each CALL, the debugger code will be called at address 0xMM20. - at each RET, the debugger code will be called at address 0xMM28. The debugger can be switched ON by writing 0xMM to the debugger control port: port_out_dbg = 0x1A; It can be switched OFF by writing a 0x00 value to this port. NOTE A conditional CALL or RET will not trigger the debugger if the condition is not satisfied. A RETI will not trigger the debugger. NOTE The debugger support is only for Z80 code. Debugger example code for RET instructions ------------------------------------------ We start with the RET instructions, these are simplest. The debugger must define a location where the PC is stored, let's use 0x1234 as example. In this example, we will place our debugging code starting at 0xD000. The first thing in the debugging code, is to store the PC to 0x1234. After encountering the RET instruction, the microcode will save the PC in two internal registers (reg_th and reg_tl). These registers can also be used by scanline interrupts, but between the RET instruction and the first debugging instruction, scanline interrupts are blocked. The next thing is obtaining the return address, and write it into the JP instruction at the end. The next thing to do, is disable debugging. We don't want that a CALL or RET inside the debugger again calls the debugger. We need the ACC, so we first save it to stack with a push. addr instruction ---- ----------- D028 ED 7F 34 12 ; store saved PC at 0x1234 E3 ex (sp),hl ; get return address, while saving HL 22 .. .. ld (dbg_ra+1),hl ; store return address in the JP instruction E1 pop hl ; restore HL F5 push AF AF xor A D3 1A out (0x1A),a ; debugging off ; here you can place code for displaying registers, return address, etc. ; it can also display the saved PC (available at 0x1234) push hl ld hl,(0x1234) ; get saved pc. (This is the address after the RET) dec hl ; can now display HL (address of the RET instruction) pop hl ; after this code, we must switch the debugger ON again ; and perform the actual RET instruction 3E D0 ld a,0xD0 ; MSB of the debugging code D3 1A out (0x1A),a ; debugging ON again ED 6B ei2 F1 pop AF dbg_ra:C3 .. .. jp xxxx ; self-modifying code NOTE If you use a RST #38 interrupt, debugging mode should be switched OFF at the beginning of the interrupt code, and switched ON again at the end, otherwise your debugger will also show every call and ret that occurs during the interrupt. Note that at the end of the interrupt, you should not use a RET instruction (that would immediately trigger a new debugger call). But you can use RETI. NOTE The interrupt system has a second set of enable/disable instructions, called EI2 and DI2. This set can be used independently from the normal EI and DI. Interrupts can only occur if both EI and EI2 are valid. The ED 7F instruction, that stores the saved PC, will also do a DI2. DI2 instruction: ED 63 EI2 instruction: ED 6B The EI2/DI2 set is intended to be used where you want to disable interrupts, but you don't know if interrupts were already disabled with DI. NOTE Scanline interrupts are not disabled by the DI or DI2 instruction. But scanline interrupts do not change the Z80 state (They don't use the stack or any other Z80 registers). Debugger example code for CALL instructions ------------------------------------------- This is similar to the RET code. addr instruction ---- ----------- D020 ED 7F 34 12 ; store saved PC at 0x1234 18 .. JR dbg_1 ; jump over the RET code at 0xD028 dbg_1: F5 push AF AF xor A D3 1A out (0x1A),a ; debugging off ; here you can place code for displaying registers, call address, etc. ; it can also display the saved PC (available at 0x1234) push hl push de ld hl,(0x1234) ; get saved pc. (This is the address after the call) dec hl ld d,(hl) ; msb of call addr dec hl ld e,(hl) ; lsb of call addr dec hl ld (dbg_ca+1),de ; store called address (must ALWAYS be done) ; can now display HL (address of the CALL instruction) ; and display DE (destination of the CALL) pop de pop hl ; after this code, we must switch the debugger ON again ; and perform the actual CALL instruction 3E D0 ld a,0xD0 ; MSB of the debugging code D3 1A out (0x1A),a ; debugging ON again F1 pop AF push hl ; save HL 2A 34 12 ld hl,(0x1234) ; get saved pc. This is the return address. E3 ex (sp),hl ; put return address on stack , while restoring HL ED 6B ei2 dbg_ca:C3 .. .. jp xxxx ; self-modifying code. JP to the destination address end. ----